#!/usr/bin/perl 
#
# (C) Copyright 2010- Hewlett-Packard Development Company, L.P.
#
# $Id: bright.pm 18154 2012-05-18 09:07:49Z cheng-xin.cai@hp.com $
#

package bright;
require Exporter;

our @ISA = qw(Exporter);
our %EXPORT_TAGS = ('all' => [qw(
    cm_AddExcludeList
    cm_RemoveExcludeList
    cm_ImageUpdate
)]);
our @EXPORT_OK = (@{$EXPORT_TAGS{'all'}});

use 5.008000;
use strict;
use warnings;

use POSIX;
use File::Basename;

my $TRUE = 1;
my $FALSE = 0;

my $cmsh_cmd = "/cm/local/apps/cmd/bin/cmsh";

sub cm_AddExcludeList
{
    my $category = shift;
    my $listname = shift;
    my @locations = @_;
  
    # Get the existing excludelist
    chomp (my @lines = `$cmsh_cmd -c \"category;use $category;get $listname\" 2>&1`);
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Getting exclude list $category:$listname")
                         : $FALSE;
    }
        
    # Add directories to the excludelist
    for my $loc (@locations) {  push @lines, "- $loc";  }
        
    # Remove duplicated
    @lines = keys %{{map { $_ => 1 } @lines }};
                
    # Write the updated excludelist to the temp file
    my $tmp_file="/tmp/add-$category-$listname.$$";
    if (! open(OFH, ">$tmp_file")) {
        return wantarray ? ($FALSE, "Can't open file $tmp_file: $!") : $FALSE;
    }
    print OFH join "\n", sort @lines;
    close(OFH);

    # Update the actual excludelist from the temp file
    system "$cmsh_cmd -c \"category;use $category;set $listname $tmp_file;commit\" 2>&1";
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Setting exclude list $category:$listname")
                         : $FALSE;
    }
    system "/bin/rm -f $tmp_file";    

    return wantarray ? ($TRUE, "") : $TRUE;
}

sub cm_RemoveExcludeList
{
    my $category = shift;
    my $listname = shift;
    my @locations = @_;
    
    # Get the existing excludelist
    chomp (my @lines = `$cmsh_cmd -c \"category;use $category;get $listname\" 2>&1`);
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Getting exclude list $category:$listname")
                         : $FALSE;
    }
        
    # Remove each directory from the excludelist
    for my $loc (@locations) {@lines = grep(!/^- $loc\s*$/, @lines); }
             
    # Write the updated excludelist to the temp file
    my $tmp_file="/tmp/remove-$category-$listname.$$";
    if (! open(OFH, ">$tmp_file")) {
        return wantarray ? ($FALSE, "Can't open file $tmp_file: $!") : $FALSE;
    }
    print OFH join "\n", sort @lines;
    close(OFH);

    # Update the actual excludelist from the temp file
    system "$cmsh_cmd -c \"category;use $category;set $listname $tmp_file;commit\" 2>&1";
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Setting exclude list $category:$listname")
                         : $FALSE;
    }
    system "/bin/rm -f $tmp_file";    

    return wantarray ? ($TRUE, "") : $TRUE;
}

sub cm_ImageUpdate($)
{
    my $noderange = shift;

    print "imageupdate $noderange\n";

    system "$cmsh_cmd -c \"device; imageupdate -w -n $noderange\" 2>&1";
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE ,"Imageupdate:$noderange")
                         : $FALSE;
    }
    #####################################################################
    # Make sure all nodes have been provisioned before proceeding further
    #####################################################################
    my $sleep_time_initial=10;
    my $sleep_time_prov=8;
    my $max_prov_iterations=10;
    my $sleep_time_imaged=8;
    my $max_noprogress_iterations=450;  # About 1 hour
 
    #
    # First make sure all Bright provisioners have started
    #
    sleep $sleep_time_initial;  # Allow time for Bright provisioners to start

    # Count the provisioners (which means nodes still TO BE provisioned)
    my $cmdawk = "$cmsh_cmd -c 'softwareimage; provisioningstatus'"
               . "| grep 'Nodes waiting to be provisioned'"
               . "| awk '{print \$NF}'";
    
    chomp(my $outawk = `$cmdawk`);
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Softwareimage Provisioningstatus")
                         : $FALSE;
    }

    my $lv_iterations=0;
    while (($outawk ne "<none>") && ($lv_iterations < $max_prov_iterations))
    {
        sleep $sleep_time_prov;
        $lv_iterations++;
        
        # Again counting the provisioners (which means nodes still TO BE provisioned)
        chomp ($outawk = `$cmdawk`); 
        if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
            return wantarray ? ($FALSE, "Softwareimage Provisioningstatus")
                             : $FALSE;
        }
    }#while


    #
    # All provisioners have started, now check until all nodes are imaged
    #
 
    # Get number of nodes being provisioned
    my $cmdinprov = "cmsh -c 'softwareimage; provisioningstatus'" 
                  . "| grep 'Nodes currently being provisioned' | wc -l";

    chomp (my $outinprov = `$cmdinprov`);
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Softwareimage Provisioningstatus")
                         : $FALSE;
    }

    my $lv_noprogress=0;
    my $completed=0;
    my $previous_outdone = -1;  # Remember number of nodes done in each iteration
    my $outdone=0;
    
    my $cmddone = "cmsh -c 'softwareimage; provisioningstatus'"
                . "| grep 'Nodes currently being provisioned'"
                . "| grep '<none>' | wc -l";
    while (($completed == 0) && ($lv_noprogress < $max_noprogress_iterations))
    {
        $outdone = `$cmddone`;
        if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
            return wantarray ? ($FALSE, "Softwareimage Provisioningstatus")
                             : $FALSE;
        }

        if ($outdone == $outinprov) {
            # Number of nodes done with provisiong equals number of nodes that were in the process of provisioning: we're done
            $completed = 1;
        }
        else{
            # Some nodes are not done yet, need to wait more
            if ($previous_outdone != $outdone) {
                # There is progress...the number of nodes nodes is changing
                $previous_outdone = $outdone;
                $lv_noprogress=0;
            }
            else { 
                # No progress, start counting how long we have no progress
                $lv_noprogress++;        
            }
            print "Waiting for all instance nodes to be imaged; expected number of imaged nodes: $outinprov, actual: $outdone; noprogress iterations: $lv_noprogress\n";
            sleep $sleep_time_imaged;
        }
    }

    if ($lv_noprogress == $max_noprogress_iterations)  { 
        # We've waited a long time and some nodes are not imaged yet: log error and exit
        return wantarray ? ($FALSE, "Imaging of all nodes did not succeed; expected number of imaged nodes: $outinprov, actual: $outdone")
                         : $FALSE;
    }
    return wantarray ? ($TRUE, "") : $TRUE;
}

sub cm_UpdateProvisioners
{
    system("cmsh -c 'softwareimage ; updateprovisioners'");
    if (WIFEXITED($?) && WEXITSTATUS($?) != 0) {
        return wantarray ? ($FALSE, "Softwareimage UpdateProvisioners")
                         : $FALSE;
    }
    return wantarray ? ($TRUE, "") : $TRUE;
}

1;

